"""
标定下载管理器（负责下载日志与PCD文件）

目标功能：
- 下载日志：将窗口中的日志文本保存到本地文件（默认下载目录）。
- 下载PCD：压缩远程主机 `/mooe/log/trigger/arms_sensor` 目录为 `.tar.gz`，并通过 SFTP 下载到本地（显示实时进度）。

实现约束：
- Python 3.10，遵循PEP 8。
- 代码采用中文注释，逻辑清晰易懂。
- 不阻塞UI：PCD下载在后台线程执行，进度通过信号更新到窗口日志。

安全策略：
- 路径处理使用 `pathlib.Path`，禁止路径穿越。
- 默认下载目录为系统用户下载目录；若目录不存在则创建。
- 调用远程压缩与下载前检查连接状态，失败时输出提示并中止。
"""

from __future__ import annotations

import datetime
from pathlib import Path
from typing import Optional

from PySide2.QtCore import QThread, Signal, QObject
from PySide2.QtWidgets import QFileDialog, QWidget

from gglobal import logger


def _get_default_download_dir() -> Path:
    """获取默认下载目录。

    优先返回用户主目录下的 `Downloads`，若不存在则尝试 `下载`；都不存在时自动创建。
    """
    home = Path.home()
    candidates = [home / "Downloads", home / "下载"]
    for p in candidates:
        try:
            if not p.exists():
                p.mkdir(parents=True, exist_ok=True)
            return p
        except Exception:
            continue
    # 兜底：使用主目录
    return home


class PcdDownloadWorker(QThread):
    """PCD下载后台线程

    职责：
    - 在远程主机上压缩指定目录；
    - 通过SFTP下载压缩包到本地；
    - 过程中将进度信息以文本形式发回主线程追加到日志。
    """

    # 进度文本信号（用于窗口日志展示）
    progress_text = Signal(str)
    # 完成信号（下载完毕或异常结束）
    done = Signal(bool, str)  # (success, message)

    def __init__(self, client_thread: QObject, remote_dir: str, local_file: Path, parent: Optional[QObject] = None) -> None:
        super().__init__(parent)
        self.client_thread = client_thread
        self.remote_dir = remote_dir
        self.local_file = local_file

    def run(self) -> None:
        try:
            # 基本校验
            if not hasattr(self.client_thread, "_connection_manager") or not self.client_thread._connection_manager.is_connected:
                self.done.emit(False, "车辆未连接，无法下载PCD。")
                return

            # 1. 远程压缩
            self.progress_text.emit("正在压缩远程PCD目录，请稍候...")
            try:
                ok = self.client_thread.compress_file(self.remote_dir, compression_type="tar.gz")
            except Exception as exc:
                ok = False
                logger.exception("远程压缩失败: %s", exc)
            if not ok:
                self.done.emit(False, "远程压缩失败，下载已取消。")
                return

            remote_tar = f"{self.remote_dir}.tar.gz"

            # 2. 下载（直接SFTP，避免高级下载导致断开）
            try:
                self.progress_text.emit(f"开始下载: {remote_tar}")
                result = self.client_thread.download_file_direct(
                    remote_path=remote_tar,
                    local_path=str(self.local_file),
                )
            except Exception as exc:
                logger.exception("下载过程异常: %s", exc)
                self.done.emit(False, "下载过程出现异常。")
                return

            if result:
                self.done.emit(True, f"下载成功: {self.local_file}")
            else:
                self.done.emit(False, "下载失败，请检查连接或远程路径。")
        except Exception:
            # 兜底异常处理
            self.done.emit(False, "下载线程异常终止。")


class CalibrationDownloadManager:
    """标定下载管理器

    为 `CalibrationActionWindow` 提供两个便捷方法：
    - save_log(window): 保存日志到本地文件。
    - download_pcd(vehicle_connection_manager, window): 压缩并下载PCD，实时显示进度。
    """

    @staticmethod
    def save_log(window: QWidget) -> None:
        """将窗口日志保存到本地文件（默认下载目录，可自行选择）。"""
        try:
            # 获取日志文本
            if not hasattr(window, "log_view"):
                logger.error("窗口缺少log_view，无法保存日志")
                return
            text = window.log_view.toPlainText()
            if not text:
                try:
                    # 友好提示
                    if hasattr(window, "append_log_safe"):
                        window.append_log_safe("当前日志为空，无需保存。")
                except Exception:
                    pass
                return

            # 构造默认文件名
            ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
            default_name = f"CalibrationLog_{ts}.log"
            default_dir = _get_default_download_dir()
            default_path = default_dir / default_name

            # 让用户选择保存路径
            try:
                filename, _ = QFileDialog.getSaveFileName(
                    window,
                    "保存日志到本地",
                    str(default_path),
                    "日志文件 (*.log);;所有文件 (*.*)",
                )
            except Exception:
                filename = str(default_path)

            if not filename:
                try:
                    if hasattr(window, "append_log_safe"):
                        window.append_log_safe("已取消保存日志。")
                except Exception:
                    pass
                return

            # 写入文件
            try:
                p = Path(filename)
                # 确保目录存在
                p.parent.mkdir(parents=True, exist_ok=True)
                p.write_text(text, encoding="utf-8")
            except Exception as exc:
                logger.exception("写入日志文件失败: %s", exc)
                try:
                    if hasattr(window, "append_log_safe"):
                        window.append_log_safe("保存日志失败，请检查文件路径与权限。")
                except Exception:
                    pass
                return

            # 成功提示
            try:
                if hasattr(window, "append_log_safe"):
                    window.append_log_safe(f"日志已保存: {filename}")
            except Exception:
                pass
        except Exception as exc:
            logger.exception("保存日志过程异常: %s", exc)

    @staticmethod
    def download_pcd(vehicle_connection_manager: QObject, window: QWidget) -> None:
        """压缩并下载PCD（默认下载目录，用户可选择保存位置）。"""
        try:
            if vehicle_connection_manager is None:
                try:
                    if hasattr(window, "append_log_safe"):
                        window.append_log_safe("未传入车辆连接管理器，无法下载PCD。")
                except Exception:
                    pass
                return

            # 获取客户端线程
            try:
                client_thread = vehicle_connection_manager.get_client_thread()
            except Exception as exc:
                logger.exception("获取客户端线程失败: %s", exc)
                client_thread = None

            if client_thread is None or not getattr(client_thread._connection_manager, "is_connected", False):
                try:
                    if hasattr(window, "append_log_safe"):
                        window.append_log_safe("车辆未连接或客户端线程不可用，无法下载PCD。")
                except Exception:
                    pass
                return

            # 选择保存目录
            default_dir = _get_default_download_dir()
            try:
                chosen_dir = QFileDialog.getExistingDirectory(window, "选择保存目录", str(default_dir))
            except Exception:
                chosen_dir = str(default_dir)
            if not chosen_dir:
                try:
                    if hasattr(window, "append_log_safe"):
                        window.append_log_safe("已取消PCD下载。")
                except Exception:
                    pass
                return

            # 构造本地文件路径
            ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
            local_file = Path(chosen_dir) / f"arms_sensor_{ts}.tar.gz"

            # 直接下载方式不使用高级传输进度信号，避免潜在断连问题

            # 启动后台线程执行压缩与下载
            worker = PcdDownloadWorker(
                client_thread=client_thread,
                remote_dir="/mooe/log/sensor_calibration",
                local_file=local_file,
                parent=window,
            )

            # 在下载生命周期内登记为连接持有者，避免中途被自动断开
            try:
                if hasattr(vehicle_connection_manager, 'acquire'):
                    vehicle_connection_manager.acquire(worker)
            except Exception:
                pass

            def _on_worker_progress(text: str) -> None:
                try:
                    if hasattr(window, "append_log_safe"):
                        window.append_log_safe(text)
                except Exception:
                    pass

            def _on_worker_done(success: bool, message: str) -> None:
                try:
                    if hasattr(window, "append_log_safe"):
                        window.append_log_safe(message)
                except Exception:
                    pass
                finally:
                    # 下载结束后释放持有者，允许连接在无人使用时自动断开
                    try:
                        if hasattr(vehicle_connection_manager, 'release'):
                            vehicle_connection_manager.release(worker)
                    except Exception:
                        pass

            try:
                worker.progress_text.connect(_on_worker_progress)
                worker.done.connect(_on_worker_done)
                worker.start()
            except Exception as exc:
                logger.exception("启动PCD下载线程失败: %s", exc)
                try:
                    if hasattr(window, "append_log_safe"):
                        window.append_log_safe("启动下载线程失败。")
                except Exception:
                    pass
        except Exception as exc:
            logger.exception("PCD下载流程异常: %s", exc)